package com.android.settings.audio.peq.util;

import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class LogPlus {
    private static int currentLogLevel = Log.DEBUG;
    private static final String TAG = "log+";

    private static final Executor threadPoolExecutor = Executors.newFixedThreadPool(3);
    private static ThreadLocal<MultiSimpleDateFormat> threadLocal = new ThreadLocal<>();

    public static void init(int logLevel) {
        currentLogLevel = logLevel;
    }

    public static void v(String tag, String msg, Throwable tr) {
        log(Log.VERBOSE, tag, msg, tr);
    }

    public static void v(String tag, String msg) {
        log(Log.VERBOSE, tag, msg, null);
    }

    public static void v(String msg, Throwable tr) {
        log(Log.VERBOSE, TAG, msg, tr);
    }

    public static void v(String msg) {
        log(Log.VERBOSE, TAG, msg, null);
    }

    public static void d(String tag, String msg, Throwable tr) {
        log(Log.DEBUG, tag, msg, tr);
    }

    public static void d(String tag, String msg) {
        log(Log.DEBUG, tag, msg, null);
    }

    public static void d(String msg, Throwable tr) {
        log(Log.DEBUG, TAG, msg, tr);
    }

    public static void d(String msg) {
        log(Log.DEBUG, TAG, msg, null);
    }

    public static void i(String tag, String msg, Throwable tr) {
        log(Log.INFO, tag, msg, tr);
    }

    public static void i(String tag, String msg) {
        log(Log.INFO, tag, msg, null);
    }

    public static void i(String msg, Throwable tr) {
        log(Log.INFO, TAG, msg, tr);
    }

    public static void i(String msg) {
        log(Log.INFO, TAG, msg, null);
    }

    public static void w(String tag, String msg, Throwable tr) {
        log(Log.WARN, tag, msg, tr);
    }

    public static void w(String tag, String msg) {
        log(Log.WARN, tag, msg, null);
    }

    public static void w(String msg, Throwable tr) {
        log(Log.WARN, TAG, msg, tr);
    }

    public static void w(String msg) {
        log(Log.WARN, TAG, msg, null);
    }

    public static void e(String tag, String msg, Throwable tr) {
        log(Log.ERROR, tag, msg, tr);
    }

    public static void e(String tag, String msg) {
        log(Log.ERROR, tag, msg, null);
    }

    public static void e(String msg, Throwable tr) {
        log(Log.ERROR, TAG, msg, tr);
    }

    public static void e(String msg) {
        log(Log.ERROR, TAG, msg, null);
    }

    private static void log(int logLevel, String tag, String msg, Throwable tr) {
        StackTraceElement e = Thread.currentThread().getStackTrace()[4];
        String fileName = e.getFileName();
        int lineNum = e.getLineNumber();
        String methodName = e.getMethodName();
        StringBuilder sb = new StringBuilder();
        sb.append(methodName).append('(').append(fileName).append(':').append(lineNum).append(')').append(msg);
        msg = sb.toString();
        sb.delete(0, sb.length());

        if (tag == null || tag.length() == 0 || tag.trim().length() == 0) {
            String className = e.getClassName();
            int index = className.lastIndexOf('.') + 1;
            sb.append(className, index, className.length());
        } else {
            sb.append(tag);
        }
        tag = sb.toString();
        if (tr == null) {
            switch (logLevel) {
                case Log.VERBOSE:
                    Log.v(tag, msg);
                    break;
                case Log.DEBUG:
                    Log.d(tag, msg);
                    break;
                case Log.INFO:
                    Log.i(tag, msg);
                    break;
                case Log.WARN:
                    Log.w(tag, msg);
                    break;
                case Log.ERROR:
                    Log.e(tag, msg);
                    break;
            }
        } else {
            switch (logLevel) {
                case Log.VERBOSE:
                    Log.v(tag, msg, tr);
                    break;
                case Log.DEBUG:
                    Log.d(tag, msg, tr);
                    break;
                case Log.INFO:
                    Log.i(tag, msg, tr);
                    break;
                case Log.WARN:
                    Log.w(tag, msg, tr);
                    break;
                case Log.ERROR:
                    Log.e(tag, msg, tr);
                    break;
            }
        }

        if (isSaveToFile) {
            /*todo 保存日志到本地*/
            String finalTag = tag;
            String finalMsg = msg;
            threadPoolExecutor.execute(() -> {
                if (true) deleteYesterdayLogFile();
                //todo 检查磁盘剩余容量, 不足则尝试删除旧日志文件
                try {
                    checkAvailableSizeThenDeleteOldLogFile();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                saveLogToFile(finalTag, finalMsg, tr);
            });
        }
    }

    /**
     * 检查剩余可用容量是否足够,不足则尝试删除旧的日志文件
     */
    private static void checkAvailableSizeThenDeleteOldLogFile() {
        long kb = 1024L;
        long mb = 1024 * kb;
        long gb = 1024 * mb;
        long minAvailableSize = gb * 2;
//        if (Util.getAvailableSize() > minAvailableSize) {
//            return;
//        }
//        Log.d("log", "###当前可用磁盘容量" + Util.getAvailableSize() + "<" + minAvailableSize + ",尝试删除旧的日志文件...");
        //todo 删除旧的日志文件
        File logFile = getTodayLogFile();
        if (logFile == null) return;
        File[] files = logFile.getParentFile().listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                continue;
            }
            if (f.getName().equals(logFile.getName())) {
                continue;
            }
            if (f.getName().endsWith(".log")) {
                Log.d("log", "###删除日志文件->" + f.getName());
                f.delete();
                //重复以上步骤
                try {
                    checkAvailableSizeThenDeleteOldLogFile();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return;
            }
        }
        Log.d("log", "###无可删除的日志文件, 剩余可用容量已小于允许的最小容量!###");
    }

    private static void saveLogToFile(String tag, String msg, Throwable th) {
        File logFile = getTodayLogFile();

        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        if (sdf == null) {
            return;
        }

        try (FileOutputStream fileOutputStream = new FileOutputStream(logFile, true)) {
            String log = String.format("[ %s ] %s ========> %s\n", sdf.yyyyMMddHHmmssSSS.format(new Date()), tag, msg);
            fileOutputStream.write(log.getBytes());
            if (th != null) {
                fileOutputStream.write(getStackTraceInfo(th).getBytes());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void deleteYesterdayLogFile() {
        try {
            File yesterdayLogFile = getYesterdayLogFile();
            if (yesterdayLogFile != null && yesterdayLogFile.exists()) {
                yesterdayLogFile.delete();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static boolean isSaveToFile;
    private static String logFileName;

    public static void setSaveLogToFile(Boolean isSaveToFile, String logFileName) {
        LogPlus.isSaveToFile = isSaveToFile;
        LogPlus.logFileName = logFileName;
    }

    private static File getTodayLogFile() {
        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        return (sdf == null) ? null : getLogFile(sdf.yyyyMMdd.format(new Date()));
    }

    private static File getYesterdayLogFile() {
        MultiSimpleDateFormat sdf = threadLocal.get();
        if (sdf == null) {
            threadLocal.set(new MultiSimpleDateFormat(new SimpleDateFormat("yyyyMMdd"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")));
        }
        sdf = threadLocal.get();
        return (sdf == null) ? null : getLogFile(sdf.yyyyMMdd.format(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24)));
    }

    private static File getLogFile(String date) {
        return new File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, date));
    }

    public static String getStackTraceInfo(Throwable throwable) {
        if (throwable == null) {
            return "";
        }
        PrintWriter pw;
        StringWriter writer = new StringWriter();
        try {
            pw = new PrintWriter(writer);
            throwable.printStackTrace(pw);
        } catch (Exception ex) {
        }
        return writer.toString();
    }

    private static class MultiSimpleDateFormat {
        private SimpleDateFormat yyyyMMdd;
        private SimpleDateFormat yyyyMMddHHmmssSSS;

        public MultiSimpleDateFormat(SimpleDateFormat yyyyMMdd, SimpleDateFormat yyyyMMddHHmmssSSS) {
            this.yyyyMMdd = yyyyMMdd;
            this.yyyyMMddHHmmssSSS = yyyyMMddHHmmssSSS;
        }
    }
}
